package vip.liux.front.infrastructure.aop.interceptor;


import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class AuditLogInfo implements Serializable {

    /**
     * 应用程序名称。
     */
    @Nullable
    private String applicationName;
    /**
     * 用户Id。
     */
    @Nullable
    private Long userId;
    /**
     * 用户名。
     */
    @Nullable
    private String username;
    /**
     * 租户Id。
     */
    @Nullable
    private String tenantId;
    /**
     * 租户名称。
     */
    @Nullable
    private String tenantName;
    /**
     * 模拟用户Id。
     */
    @Nullable
    private Long impersonatorUserId;
    /**
     * 模拟租户Id。
     */
    @Nullable
    private Long impersonatorTenantId;
    /**
     * 模拟用户名。
     */
    @Nullable
    private String impersonatorUsername;
    /**
     * 模拟租户名称。
     */
    @Nullable
    private String impersonatorTenantName;
    /**
     * 执行时间。
     */
    @Getter
    private LocalDateTime executionTime;
    /**
     * 执行持续时间（毫秒）。
     */
    private int executionDuration;
    /**
     * 客户端Id。
     */
    @Nullable
    private String clientId;
    /**
     * 链路Id。
     */
    @Nullable
    private String correlationId;
    /**
     * 客户端IP地址。
     */
    @Nullable
    private String clientIpAddress;
    /**
     * 客户端名称。
     */
    @Nullable
    private String clientName;
    /**
     * 浏览器信息。
     */
    @Nullable
    private String browserInfo;
    /**
     * HTTP方法。
     */
    @Nullable
    private String httpMethod;
    /**
     * HTTP状态码。
     */
    @Nullable
    private Integer httpStatusCode;
    /**
     * URL。
     */
    @Nullable
    private String url;

    /**
     * 操作信息。
     */
    @Getter
    @Setter
    private List<AuditLogActionInfo> actions;

    /**
     * 异常。
     */
    @Getter
    private final List<Exception> exceptions;

    /**
     * 实体变更信息。
     */
    @Getter
    private final List<EntityChangeInfo> entityChanges;

    /**
     * 评论。
     */
    @Getter
    private List<String> comments;

    public AuditLogInfo() {
        this.actions = new ArrayList<>();
        this.exceptions = new ArrayList<>();
        this.entityChanges = new ArrayList<>();
        this.comments = new ArrayList<>();
    }

    @Nullable
    public String getApplicationName() {
        return applicationName;
    }

    public void setApplicationName(@Nullable String applicationName) {
        this.applicationName = applicationName;
    }

    @Nullable
    public Long getUserId() {
        return userId;
    }

    public void setUserId(@Nullable Long userId) {
        this.userId = userId;
    }

    @Nullable
    public String getUsername() {
        return username;
    }

    public void setUsername(@Nullable String username) {
        this.username = username;
    }

    @Nullable
    public String getTenantId() {
        return tenantId;
    }

    public void setTenantId(@Nullable String tenantId) {
        this.tenantId = tenantId;
    }

    @Nullable
    public String getTenantName() {
        return tenantName;
    }

    public void setTenantName(@Nullable String tenantName) {
        this.tenantName = tenantName;
    }

    @Nullable
    public Long getImpersonatorUserId() {
        return impersonatorUserId;
    }

    public void setImpersonatorUserId(@Nullable Long impersonatorUserId) {
        this.impersonatorUserId = impersonatorUserId;
    }

    @Nullable
    public Long getImpersonatorTenantId() {
        return impersonatorTenantId;
    }

    public void setImpersonatorTenantId(@Nullable Long impersonatorTenantId) {
        this.impersonatorTenantId = impersonatorTenantId;
    }

    @Nullable
    public String getImpersonatorUsername() {
        return impersonatorUsername;
    }

    public void setImpersonatorUsername(@Nullable String impersonatorUsername) {
        this.impersonatorUsername = impersonatorUsername;
    }

    @Nullable
    public String getImpersonatorTenantName() {
        return impersonatorTenantName;
    }

    public void setImpersonatorTenantName(@Nullable String impersonatorTenantName) {
        this.impersonatorTenantName = impersonatorTenantName;
    }

    public void setExecutionTime(LocalDateTime executionTime) {
        this.executionTime = executionTime;
    }

    public Integer getExecutionDuration() {
        return executionDuration;
    }

    public void setExecutionDuration(Integer executionDuration) {
        this.executionDuration = executionDuration;
    }

    @Nullable
    public String getClientId() {
        return clientId;
    }

    public void setClientId(@Nullable String clientId) {
        this.clientId = clientId;
    }

    @Nullable
    public String getCorrelationId() {
        return correlationId;
    }

    public void setCorrelationId(@Nullable String correlationId) {
        this.correlationId = correlationId;
    }

    @Nullable
    public String getClientIpAddress() {
        return clientIpAddress;
    }

    public void setClientIpAddress(@Nullable String clientIpAddress) {
        this.clientIpAddress = clientIpAddress;
    }

    @Nullable
    public String getClientName() {
        return clientName;
    }

    public void setClientName(@Nullable String clientName) {
        this.clientName = clientName;
    }

    @Nullable
    public String getBrowserInfo() {
        return browserInfo;
    }

    public void setBrowserInfo(@Nullable String browserInfo) {
        this.browserInfo = browserInfo;
    }

    @Nullable
    public String getHttpMethod() {
        return httpMethod;
    }

    public void setHttpMethod(@Nullable String httpMethod) {
        this.httpMethod = httpMethod;
    }

    @Nullable
    public Integer getHttpStatusCode() {
        return httpStatusCode;
    }

    public void setHttpStatusCode(@Nullable Integer httpStatusCode) {
        this.httpStatusCode = httpStatusCode;
    }

    @Nullable
    public String getUrl() {
        return url;
    }

    public void setUrl(@Nullable String url) {
        this.url = url;
    }

    public void setComments(List<String> comments) {
        this.comments = comments;
    }


    @Override
    public String toString() {
        var sb = new StringBuilder();
        sb.append(String.format("AUDIT LOG: [%s: %s] %s\n", Optional.ofNullable(this.getHttpStatusCode()).map(Objects::toString).orElse("---"), StringUtils.rightPad(Optional.ofNullable(this.getHttpMethod()).orElse("-------"), 7, ""), this.url));
        sb.append(String.format("- Username - UserId                 : %s - %s\n", this.getUsername(), this.getUserId()));
        sb.append(String.format("- ClientIpAddress        : %s\n", this.getClientIpAddress()));
        sb.append(String.format("- ExecutionDuration      : %s\n", this.getExecutionDuration()));

        if (!this.actions.isEmpty()) {
            sb.append("- Actions:\n");
            for (AuditLogActionInfo action : this.actions) {
                sb.append(String.format("  - %s.%s (%s ms.)\n", action.getServiceName(), action.getMethodName(), action.getExecutionDuration()));
                sb.append(String.format("    %s\n", action.getParameters()));
            }
        }

        if (!this.exceptions.isEmpty()) {
            sb.append("- Exceptions:\n");
            for (Exception exception : this.exceptions) {
                sb.append(String.format("  - %s\n", exception.getMessage()));
                sb.append(String.format("    %s\n", exception));
            }
        }

        if (!this.entityChanges.isEmpty()) {
            sb.append("- Entity Changes:\n");
            for (EntityChangeInfo entityChange : this.entityChanges) {
                sb.append(String.format("  - [%s] %s, Id = %s\n", entityChange.getChangeType(), entityChange.getEntityTypeFullName(), entityChange.getEntityId()));
                for (EntityPropertyChangeInfo propertyChange : entityChange.getPropertyChanges()) {

                    sb.append(String.format("    %s: %s -> %s\n", propertyChange.getPropertyName(), propertyChange.getOriginalValue(), propertyChange.getNewValue()));
                }
            }
        }

        return sb.toString();
    }
}
